
#include <kernel/kernel.h>
#include <kernel/limits.h>
#include <kernel/error/error.h>
#include <kernel/userid/userid.h>
#include "block.h"

#include <kernel/debug.h>

static int UserSpace [MEMSIZE];


#define SIZEOF(p) *((int *) (p - sizeof (int)))
#define SIZEAT(p) *((int *) (p))

extern void block_init ()
{
	index i;

	for (i = 0; i < MEMSIZE; ++i)
	{
		UserSpace [i] = 0;
	}

	*((int *) UserSpace) = MEMSIZE;

	POST ("block", "memory initialized");
}

extern void *block_request (size _sz)
{
	int i, retries = 5;
	int *p;
	int psz, sz2;

	if (_sz < 1) return NULL;

	/* the user wants bytes and we keep track of ints, so we need a conversion: */
	_sz = _sz / sizeof (int) + sizeof (int);

	for (i = 0; i < retries; ++i) for (p = UserSpace; p < UserSpace + MEMSIZE - 1;)
	{
		psz = *p;

		if (psz < 0)
		{
			/* this block is used; skip to the next one */

			p -= psz;

			continue;
		}
		
		if (psz >= _sz)
		{
			/* this block is big enough */

			if (psz < 2 * _sz || psz <= BLOCKSIZE)
			{
				/* this block is perfect! */

				/* mark it as used */
				*p *= -1;

				return p + 1;
			}
			else if (psz >= 2 * BLOCKSIZE)
			{
				/* it's too big; we need to split it */

				/* split the current block */
				sz2 = psz / 2;
				*p = sz2;

				/* create the resulting block */
				*(p + sz2) = psz - sz2;

				/* now we look at the newly created block */
				p += sz2;
			}
			else
			{
				/* couldn't split, so we'll just go to the next block */
				p += psz;
			}
		}
		else
		{
			/* this block is too small; can we compact it? */

			if (p + psz < UserSpace + MEMSIZE - 1 && (sz2 = *(p + psz)) > 0)
			{
				/* this block has a free neighbor! */
				
				*p = psz + sz2;
			}
			else
			{
				/* no luck; try the next block ... */
				p += psz;
			}
		}
	}

	error_log (ERROR_NOBLOCK);

	return NULL;
}


extern void block_release (void *_p)
{
	if (_p == NULL)
	{
		return;
	}

	*((int *) _p - 1) *= -1;
}


extern void *block_resize (void *_p, size _sz)
{
	int *p = (int *) _p;
	int i;
	int sz = *(p - 1) * -1;
	int sz2;

	if (sz > _sz + sizeof (int))
	{
		/* we have enough room; don't worry about it */

		return _p;
	}
	else if (p + sz < UserSpace + MEMSIZE && (sz2 = *(p - 1 + sz)) > 0)
	{
		/* we can just steal our neighboor's memory */

		POST ("block", "expanding block");

		*(p - 1) -= sz2;

		return _p;
	}
	else
	{
		/* looks like we need to move to a bigger region */

		POST ("block", "moving memory block");

		p = block_request (_sz);

		if (p == NULL)
		{
			return NULL;
		}

		for (i = 0; i < _sz; ++i)
		{
			((char *) p) [i] = ((char *) _p) [i];
		}

		block_release (_p);
		
		return p;
	}
}

/* this can be called from gdb */
IFDEBUG (extern void dumpmem ()
{
	int i;
	int sz;

	for (i = 0; i < MEMSIZE;)
	{
		sz = *((int *) (UserSpace + i));

		printf ("%i ", sz);
//		scanf ("%*c");

		i += abs (sz);
	}
})
